package com.stateflow.facade.common.template;

import com.stateflow.facade.common.exception.CommonException;
import com.stateflow.facade.common.model.req.CommonRequest;
import com.stateflow.facade.common.model.res.CommonResult;
import com.stateflow.facade.common.util.CommonAssert;
import com.stateflow.facade.common.util.CommonUtils;
import com.stateflow.facade.common.util.StringUtils;
import org.slf4j.Logger;
import org.springframework.dao.DuplicateKeyException;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by weiqingming on 2019/4/16. 公共模板
 */
public abstract class CommonTemplate {

    /** 存储线程对应的traceId */
    private static final Map<Long, String> THREAD_TRACE_IDS = new ConcurrentHashMap<>();

    /** 真实入参 */
    protected Object param;

    /**
     * 获取入参真实类型对象
     *
     * @param <T>
     * @return
     */
    protected <T> T getParam() {
        return (T) param;
    }


    protected <T> T commonCheck(CommonRequest request, Class clazz) {
        CommonAssert.isNoEmptyObj(request.getParam(), "入参不能为空");
        CommonAssert.isTrue(request.getParam().getClass() == clazz, "入参类型不正确");
        T req = (T) request.getParam();
        this.param = req;
        return req;
    }

    /**
     * 校验
     */
    protected abstract void check();

    /**
     * 逻辑
     */
    protected abstract CommonResult business();

    /**
     * 执行
     *
     * @param request
     * @param template
     * @return
     */
    public static CommonResult execute(CommonRequest request, Logger logger, CommonTemplate template) {

        try {

            CommonAssert.isNoEmptyObj(request, "request入参为空");

            if (!StringUtils.isNotBlank(request.getTraceId())) {
                request.setTraceId(CommonUtils.getUUID().replace("-", ""));
            }

            THREAD_TRACE_IDS.put(Thread.currentThread().getId(), request.getTraceId());

            // 取到调用方信息类名+方法名
            StackTraceElement element = Thread.currentThread().getStackTrace()[2];
            String callers = new StringBuilder()
                    .append(element.getClassName())
                    .append(".")
                    .append(element.getMethodName()).toString();

            // 入参日志打印
            logger.info(new StringBuilder().append(callers).append("入参: ").append(request).toString());

            // 执行校验代码
            template.check();

            // 执行逻辑代码
            CommonResult result = template.business();

            // 打印结果
            logger.info(new StringBuilder()
                    .append(callers)
                    .append("结果: ").append(result)
                    .append(", traceId: ").append(request.getTraceId()).toString());

            // 返回
            return result;

        } catch (Throwable e) {
            return failHandle(e, logger, request.getTraceId());
        }

    }

    private static CommonResult failHandle(Throwable cause, Logger logger, String traceId) {

        String errorMsg = new StringBuilder()
                .append(cause.getMessage())
                .append(" traceId: ").append(traceId).toString();

        // 判断和设置结果
        if (cause instanceof CommonException) {

            // 打印日志
            logger.warn(errorMsg, cause);

            CommonException ce = (CommonException) cause;
            return new CommonResult(ce.getErrorCode(), ce.getErrorMsg());

        }
        if (cause instanceof DuplicateKeyException) {

            // 打印错误日志
            logger.error(errorMsg, cause);

            return new CommonResult("IDEMPOTENT_ERROR", "已有相同数据，不能重复添加");

        } else {

            // 打印错误日志
            logger.error(errorMsg, cause);

            return new CommonResult("ERROR", "执行失败");
        }
    }

    public static String getTraceId(){
        return THREAD_TRACE_IDS.get(Thread.currentThread().getId());
    }
}
